﻿
package extremefx.tools {
	import flash.errors.IllegalOperationError;
	import flash.geom.Point;		

	/**
	 * @author Marcelo Volmaro
	 */
	public final class MathUtils {
		/**
		 * Calculates the geometric mean of an array of numbers
		 * @param pNumbers the array containing the numbers
		 * @return the geometric mean
		 * @throws IllegalOperationError if the array contains negative numbers.
		 */
		public static function geometricMean(pNumbers:Vector.<Number>):Number {
			if (pNumbers.length == 0) return 1;
			
			var l:Number = 1 / pNumbers.length, gm:Number = 1;
			
			for each(var c:Number in pNumbers){
				if (c == 0) c = 1;
				if (c < 0) throw new IllegalOperationError("Geometric mean cannot be calculated for negative values");
				gm *= Math.pow(c, l); 
			}
			
			return gm;
		}
		
		/**
		 * Rounds a target number down to the nearest multiple of another number.
		 * @see Math#floor
		 */
		public static function roundDownToNearest(numberToRound:Number, nearest:Number):Number {
			return Math.floor(numberToRound / nearest) * nearest;
		}
		
		/**
		 * Rounds a target number to the nearest multiple of another number.
		 * @see Math#round
		 */
		public static function roundToNearest(numberToRound:Number, nearest:Number):Number {
			return Math.round(numberToRound / nearest) * nearest;
		}
		
		/**
		 * Rounds a target number to a specific number of decimal places.
		 * @see Math#round
		 */
		public static function roundToPrecision(number:Number, precision:int = 0):Number {
			var decimalPlaces:Number = Math.pow(10, precision);
			return Math.round(decimalPlaces * number) / decimalPlaces;
		} 
		
		/**
		 * Rounds a target number up to the nearest multiple of another number.
		 * @see Math#ceil
		 */
		public static function roundUpToNearest(numberToRound:Number, nearest:Number):Number {
			return Math.ceil(numberToRound / nearest) * nearest;
		}
		
		/**
		 * Converts degress into radians.
		 * @param pDegrees The degree value.
		 * @return The degrees as radians.
		 */
		public static function degreesToRadians(pDegrees:Number):Number {
			return pDegrees * (Math.PI / 180);
		}
		
		/**
		 * Converts radians to degress.
		 * @param pRadians The radians value.
		 * @return The radians as degrees.
		 */
		public static function radiansToDegrees(pRadians:Number):Number {
			return pRadians * (180 / Math.PI);
		}
		
		
		/**
		 * Gets a point offset by a distance and angle (in degrees).
		 * @param pAngle The angle in degrees.
		 * @param pDistance The distance.
		 * @return The offset point.
		 */
		public static function getOffset(pAngle:Number, pDistance:Number):Point {
			var x:Number = Math.cos(degreesToRadians(pAngle)) * pDistance;
            var y:Number = Math.tan(degreesToRadians(pAngle)) * x;
            return new Point(x, y);
		}
		
		/**
		 * Gets the angle between to points.
		 * @param pOffset The offset point.
		 * @return The angle for the offset.
		 */
		public static function getAngleFromOffset(pOffset:Point):Number {
			var opposite:Number = Math.abs(pOffset.y);
            var adjacent:Number = Math.abs(pOffset.x);

			if (pOffset.y < 0) {
                opposite = -opposite;
            }

            var angle:Number = radiansToDegrees(Math.atan(opposite / adjacent));
            
            if (isNaN(angle)) {
                return 0;
            }

            if (pOffset.x < 0) {
                angle = 90 + (90 - angle);
            }

            return angle;
		}
	}
}
